package com.twetec.cms.service.search.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;

import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
import co.elastic.clients.elasticsearch.core.bulk.DeleteOperation;
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.json.JsonData;
import com.twetec.cms.model.po.search.SearchArticlePO;
import com.twetec.cms.model.vo.search.SearchArticlePageListVO;
import com.twetec.cms.model.vo.search.SearchArticleVO;
import com.twetec.cms.service.search.SearchArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class SearchArticleServiceImpl implements SearchArticleService {

    private static final String SEARCH_INDEX_NAME = "search_article";

    @Autowired
    private ElasticsearchClient searchClient;

    @Override
    public void addDoc(SearchArticlePO searchArticlePO) throws Exception {
        CreateRequest<SearchArticlePO> createRequest = new CreateRequest.Builder<SearchArticlePO>()
                .index(SEARCH_INDEX_NAME)
                .id(searchArticlePO.getArticleId())
                .document(searchArticlePO)
                .build();

        CreateResponse createResponse = searchClient.create(createRequest);
    }

    @Override
    public void addDocBatch(List<SearchArticlePO> searchArticlePOList) throws Exception {
        List<BulkOperation> bulkOperationList = new ArrayList<>();

        for (SearchArticlePO searchArticlePO : searchArticlePOList) {
            BulkOperation bulkOperation = new BulkOperation.Builder()
                    .create(
                            new CreateOperation.Builder<SearchArticlePO>()
                                    .id(searchArticlePO.getArticleId())
                                    .document(searchArticlePO)
                                    .build()
                    ).build();

            bulkOperationList.add(bulkOperation);
        }

        BulkRequest bulkRequest = new BulkRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .operations(bulkOperationList)
                .build();

        BulkResponse bulkResponse = searchClient.bulk(bulkRequest);

        if (bulkResponse.errors()) throw new Exception();
    }

    @Override
    public void deleteDocByArticleId(String articleId) throws Exception {
        DeleteRequest deleteRequest = new DeleteRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .id(articleId)
                .build();

        DeleteResponse deleteResponse = searchClient.delete(deleteRequest);
    }

    @Override
    public void deleteDocBatchByArticleIds(List<String> articleIdList) throws Exception {
        List<BulkOperation> bulkOperationList = new ArrayList<>();

        for (String articleId : articleIdList) {
            BulkOperation bulkOperation = new BulkOperation.Builder()
                    .delete(
                            new DeleteOperation.Builder()
                                    .id(articleId)
                                    .build()
                    ).build();

            bulkOperationList.add(bulkOperation);
        }


        BulkRequest bulkRequest = new BulkRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .operations(bulkOperationList)
                .build();

        BulkResponse bulkResponse = searchClient.bulk(bulkRequest);

        if (bulkResponse.errors()) throw new Exception();
    }

    @Override
    public void updateDoc(SearchArticlePO searchArticlePO) throws Exception {
        IndexRequest<SearchArticlePO> indexRequest = new IndexRequest.Builder<SearchArticlePO>()
                .index(SEARCH_INDEX_NAME)
                .id(searchArticlePO.getArticleId())
                .document(searchArticlePO)
                .build();


        IndexResponse indexResponse = searchClient.index(indexRequest);
    }

    @Override
    public void updateDocBatch(List<SearchArticlePO> searchArticlePOList) throws Exception {
        List<BulkOperation> bulkOperationList = new ArrayList<>();

        for (SearchArticlePO searchArticlePO : searchArticlePOList) {
            BulkOperation bulkOperation = new BulkOperation.Builder()
                    .index(
                            new IndexOperation.Builder<SearchArticlePO>()
                                    .id(searchArticlePO.getArticleId())
                                    .document(searchArticlePO)
                                    .build()
                    ).build();

            bulkOperationList.add(bulkOperation);
        }


        BulkRequest bulkRequest = new BulkRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .operations(bulkOperationList)
                .build();

        BulkResponse bulkResponse = searchClient.bulk(bulkRequest);

        if (bulkResponse.errors()) throw new Exception();
    }

    @Override
    public SearchArticleVO getDocByArticleId(String articleId) throws Exception {
        GetRequest getRequest = new GetRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .id(articleId)
                .build();

        GetResponse<SearchArticleVO> getResponse = searchClient.get(getRequest, SearchArticleVO.class);

        // 如果数据不存在，则抛出异常
        if (!getResponse.found()) throw new Exception();

        return getResponse.source();
    }

    /**
     * 后台 - 根据检索条件，分页展示文章列表(未输入的，可以为空字符串)
     */
    @Override
    public SearchArticlePageListVO pageSearchList(String articleTitle, String articleContent, String authorName, List<String> columnIdList, Date createStartTime, Date createEndTime, Date releaseStartTime, Date releaseEndTime, String articleOrigin, String articleStatus, String seoKeywords, String articleType, Integer pageNo, Integer pageSize) throws Exception {
        // 1、构建检索条件集
        List<Query> shouldQueryList = new ArrayList<>();

        // 1.1 ~ 检索条件 - 文章标题名
        Query articleTitleMatchQuery = buildArticleTitleMatchQuery(articleTitle);
        shouldQueryList.add(articleTitleMatchQuery);

        // 1.2 ~ 检索条件 - 文章内容
        Query articleContentMatchQuery = buildArticleContentMatchQuery(articleContent);
        shouldQueryList.add(articleContentMatchQuery);

        // 2、构建必须满足的检索条件
        List<Query> mustQueryList = new ArrayList<>();

        // 2.1 ~ 检索条件 - 作者名字
        if (!StrUtil.isBlankOrUndefined(authorName)) {
            Query authorNameMatchQuery = buildAuthorNameMatchQuery(authorName);
            mustQueryList.add(authorNameMatchQuery);
        }

        // 2.2 ~ 检索条件 - 来源
        if (!StrUtil.isBlankOrUndefined(articleOrigin)) {
            Query articleOriginMatchQuery = buildArticleOriginMatchQuery(articleOrigin);
            mustQueryList.add(articleOriginMatchQuery);
        }

        // 2.3 ~ 检索条件 - SEO关键字
        if (!StrUtil.isBlankOrUndefined(seoKeywords)) {
            Query seoKeywordsMatchQuery = buildSeoKeywordsMatchQuery(seoKeywords);
            mustQueryList.add(seoKeywordsMatchQuery);
        }

        // 3、构建过滤的检索条件
        List<Query> filterQueryList = new ArrayList<>();

        // 3.1 ~ 检索条件 - 所属栏目
        if (CollectionUtil.isNotEmpty(columnIdList)) {
            Query columnIdTermsQuery = buildColumnIdTermsQuery(columnIdList);
            filterQueryList.add(columnIdTermsQuery);
        }

        // 3.2 ~ 检索条件 - 创建时间
        if (null != createStartTime || null != createEndTime) {
            Query createTimeRangeQuery = buildCreateTimeRangeQuery(createStartTime, createEndTime);
            filterQueryList.add(createTimeRangeQuery);
        }

        // 3.3 ~ 检索条件 - 发布时间
        if (null != releaseStartTime || null != releaseEndTime) {
            Query releaseTimeRangeQuery = buildReleaseTimeRangeQuery(releaseStartTime, releaseEndTime);
            filterQueryList.add(releaseTimeRangeQuery);
        }

        // 3.4 ~ 检索条件 - 状态
        if (!StrUtil.isBlankOrUndefined(articleStatus)) {
            Query articleStatusTermQuery = buildArticleStatusTermQuery(articleStatus);
            filterQueryList.add(articleStatusTermQuery);
        }

        // 3.5 ~ 检索条件 - 资讯类型
        if (!StrUtil.isBlankOrUndefined(articleType)) {
            Query articleTypeTermQuery = buildArticleTypeTermQuery(articleType);
            filterQueryList.add(articleTypeTermQuery);
        }


        // 构建整体请求
        Query query = new Query.Builder()
                .bool(
                        new BoolQuery.Builder()
                                .should(shouldQueryList)
                                .must(mustQueryList)
                                .filter(filterQueryList)
                                .build()
                )
                .build();

        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .query(query)
                .from(pageNo <= 0 ? 0 : (pageNo - 1) * pageSize)
                .size(pageSize);

        SearchRequest searchRequest = searchRequestBuilder.build();

        SearchResponse<SearchArticleVO> searchResponse = searchClient.search(searchRequest, SearchArticleVO.class);

        return buildPageListVO(searchResponse, pageNo, pageSize);
    }

    /**
     * 前台 - 根据检索条件，分页展示文章列表(未输入的，可以为空字符串)
     */
    @Override
    public SearchArticlePageListVO pageSearchList(String siteId, String articleTitle, String articleContent, String authorName, List<String> columnIdList, Date createStartTime, Date createEndTime, Date releaseStartTime, Date releaseEndTime, String articleOrigin, String articleStatus, String seoKeywords, String articleType, Integer pageNo, Integer pageSize) throws Exception {
        // 1、构建检索条件集
        List<Query> shouldQueryList = new ArrayList<>();

        // 1.1 ~ 检索条件 - 文章标题名
        Query articleTitleMatchQuery = buildArticleTitleMatchQuery(articleTitle);
        shouldQueryList.add(articleTitleMatchQuery);

        // 1.2 ~ 检索条件 - 文章内容
        Query articleContentMatchQuery = buildArticleContentMatchQuery(articleContent);
        shouldQueryList.add(articleContentMatchQuery);

        // 2、构建必须满足的检索条件
        List<Query> mustQueryList = new ArrayList<>();

        // 2.1 ~ 检索条件 - 作者名字
        if (!StrUtil.isBlankOrUndefined(authorName)) {
            Query authorNameMatchQuery = buildAuthorNameMatchQuery(authorName);
            mustQueryList.add(authorNameMatchQuery);
        }

        // 2.2 ~ 检索条件 - 来源
        if (!StrUtil.isBlankOrUndefined(articleOrigin)) {
            Query articleOriginMatchQuery = buildArticleOriginMatchQuery(articleOrigin);
            mustQueryList.add(articleOriginMatchQuery);
        }

        // 2.3 ~ 检索条件 - SEO关键字
        if (!StrUtil.isBlankOrUndefined(seoKeywords)) {
            Query seoKeywordsMatchQuery = buildSeoKeywordsMatchQuery(seoKeywords);
            mustQueryList.add(seoKeywordsMatchQuery);
        }

        // 3、构建过滤的检索条件
        List<Query> filterQueryList = new ArrayList<>();

        // 3.1 ~ 检索条件 - 所属栏目
        if (CollectionUtil.isNotEmpty(columnIdList)) {
            Query columnIdTermsQuery = buildColumnIdTermsQuery(columnIdList);
            filterQueryList.add(columnIdTermsQuery);
        }

        // 3.2 ~ 检索条件 - 创建时间
        if (null != createStartTime || null != createEndTime) {
            Query createTimeRangeQuery = buildCreateTimeRangeQuery(createStartTime, createEndTime);
            filterQueryList.add(createTimeRangeQuery);
        }

        // 3.3 ~ 检索条件 - 发布时间
        if (null != releaseStartTime || null != releaseEndTime) {
            Query releaseTimeRangeQuery = buildReleaseTimeRangeQuery(releaseStartTime, releaseEndTime);
            filterQueryList.add(releaseTimeRangeQuery);
        }

        // 3.4 ~ 检索条件 - 状态
        if (!StrUtil.isBlankOrUndefined(articleStatus)) {
            Query articleStatusTermQuery = buildArticleStatusTermQuery(articleStatus);
            filterQueryList.add(articleStatusTermQuery);
        }

        // 3.5 ~ 检索条件 - 资讯类型
        if (!StrUtil.isBlankOrUndefined(articleType)) {
            Query articleTypeTermQuery = buildArticleTypeTermQuery(articleType);
            filterQueryList.add(articleTypeTermQuery);
        }

        // 3.6 ~ 检索条件 - 所属站点id
        if (!StrUtil.isBlankOrUndefined(siteId)) {
            Query siteIdTermQuery = buildSiteIdTermQuery(articleType);
            filterQueryList.add(siteIdTermQuery);
        }

        // 构建整体请求
        Query query = new Query.Builder()
                .bool(
                        new BoolQuery.Builder()
                                .should(shouldQueryList)
                                .must(mustQueryList)
                                .filter(filterQueryList)
                                .build()
                )
                .build();

        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder()
                .index(SEARCH_INDEX_NAME)
                .query(query)
                .from(pageNo <= 0 ? 0 : (pageNo - 1) * pageSize)
                .size(pageSize);

        SearchRequest searchRequest = searchRequestBuilder.build();

        SearchResponse<SearchArticleVO> searchResponse = searchClient.search(searchRequest, SearchArticleVO.class);

        return buildPageListVO(searchResponse, pageNo, pageSize);
    }

    /**
     * 构建分页结果
     */
    private SearchArticlePageListVO buildPageListVO(SearchResponse<SearchArticleVO> searchResponse, Integer page, Integer limit) {
        // 返回数据
        SearchArticlePageListVO searchArticlePageListVO = new SearchArticlePageListVO();

        // 解析响应-数据
        List<SearchArticleVO> searchArticleVOList = new ArrayList<>();
        // 遍历结果集
        List<Hit<SearchArticleVO>> searchHitList = searchResponse.hits().hits();
        for (Hit<SearchArticleVO> searchHit : searchHitList) {
            SearchArticleVO searchArticleVO = searchHit.source();
            searchArticleVOList.add(searchArticleVO);
        }
        // 设置数据集
        searchArticlePageListVO.setList(searchArticleVOList);

        // 解析响应-总条目数
        TotalHits totalHits = searchResponse.hits().total();
        int totalRows = Long.valueOf(totalHits.value()).intValue();

        // 设定总条目数
        searchArticlePageListVO.setTotal(totalRows);

        // 设定每页显示条目数
        searchArticlePageListVO.setLimit(limit);

        // 设定总页数
        Integer totalPage = totalRows % limit == 0? totalRows / limit : totalRows / limit + 1;

        // 设定当前页码（保护下标，不管数据）
        searchArticlePageListVO.setPage(page);
        if (page <= 0) {
            searchArticlePageListVO.setPage(1);
        }
        else if ((page - 1) * limit > totalRows) {
            searchArticlePageListVO.setPage(totalPage);
        }

        return searchArticlePageListVO;
    }

    /**
     * 构建作者名字Match查询
     */
    private Query buildAuthorNameMatchQuery(String authorName) {
        return new Query.Builder()
                .match(
                        new MatchQuery.Builder()
                                .field("authorName.IK")
                                .query(
                                        new FieldValue.Builder()
                                                .stringValue(authorName)
                                                .build()
                                )
                                .analyzer("ikSearchAnalyzer")
                                .build()
                )
                .build();

    }

    /**
     * 构建来源Match查询
     */
    private Query buildArticleOriginMatchQuery(String articleOrigin) {
        return new Query.Builder()
                .match(
                        new MatchQuery.Builder()
                                .field("articleOrigin.IK")
                                .query(
                                        new FieldValue.Builder()
                                                .stringValue(articleOrigin)
                                                .build()
                                )
                                .analyzer("ikSearchAnalyzer")
                                .build()
                )
                .build();

    }

    /**
     * 构建SEO关键字Match查询
     */
    private Query buildSeoKeywordsMatchQuery(String seoKeywords) {
        return new Query.Builder()
                .match(
                        new MatchQuery.Builder()
                                .field("seoKeywords.IK")
                                .query(
                                        new FieldValue.Builder()
                                                .stringValue(seoKeywords)
                                                .build()
                                )
                                .analyzer("ikSearchAnalyzer")
                                .build()
                )
                .build();

    }

    /**
     * 构建文章标题Match查询
     */
    private Query buildArticleTitleMatchQuery(String articleTitle) {
        return new Query.Builder()
                .match(
                        new MatchQuery.Builder()
                                .field("articleTitle.IK")
                                .query(
                                        new FieldValue.Builder()
                                                .stringValue(articleTitle)
                                                .build()
                                )
                                .analyzer("ikSearchAnalyzer")
                                .build()
                )
                .build();
    }

    /**
     * 构建文章内容Match查询
     */
    private Query buildArticleContentMatchQuery(String articleContent) {
        return new Query.Builder()
                .match(
                        new MatchQuery.Builder()
                                .field("articleContent")
                                .query(
                                        new FieldValue.Builder()
                                                .stringValue(articleContent)
                                                .build()
                                )
                                .analyzer("ikSearchAnalyzer")
                                .build()
                )
                .build();
    }

    /**
     * 构建文章所属栏目Terms查询
     */
    private Query buildColumnIdTermsQuery(List<String> columnIdList) {
        List<FieldValue> fieldValueList = new ArrayList<>();

        for (String columnId : columnIdList) {
            fieldValueList.add(
                    new FieldValue.Builder()
                            .stringValue(columnId)
                            .build()
            );
        }

        return new Query.Builder()
                .terms(
                        new TermsQuery.Builder()
                                .field("columnId")
                                .terms(
                                        new TermsQueryField.Builder()
                                                .value(fieldValueList)
                                                .build()
                                )
                                .build()
                )
                .build();
    }

    /**
     * 构建文章创建时间Range查询
     */
    private Query buildCreateTimeRangeQuery(Date createStartTime, Date createEndTime) {
        RangeQuery.Builder rangeQueryBuilder = new RangeQuery.Builder()
                .field("createTime")
                .format("yyyy-MM-dd HH:mm:ss");

        // 创建时间 - 开始
        if (null != createStartTime) {
            rangeQueryBuilder.gte(JsonData.of(DateUtil.format(createStartTime, "yyyy-MM-dd HH:mm:ss")));
        }

        // 创建时间 - 结束
        if (null != createEndTime) {
            rangeQueryBuilder.lte(JsonData.of(DateUtil.format(createEndTime, "yyyy-MM-dd HH:mm:ss")));
        }

        Query query = new Query.Builder()
                .range(rangeQueryBuilder.build())
                .build();

        return query;
    }

    /**
     * 构建文章发布时间Range查询
     */
    private Query buildReleaseTimeRangeQuery(Date releaseStartTime, Date releaseEndTime) {
        RangeQuery.Builder rangeQueryBuilder = new RangeQuery.Builder()
                .field("releaseTime")
                .format("yyyy-MM-dd HH:mm:ss");

        // 创建时间 - 开始
        if (null != releaseStartTime) {
            rangeQueryBuilder.gte(JsonData.of(DateUtil.format(releaseStartTime, "yyyy-MM-dd HH:mm:ss")));
        }

        // 创建时间 - 结束
        if (null != releaseEndTime) {
            rangeQueryBuilder.lte(JsonData.of(DateUtil.format(releaseEndTime, "yyyy-MM-dd HH:mm:ss")));
        }

        Query query = new Query.Builder()
                .range(rangeQueryBuilder.build())
                .build();

        return query;
    }

    /**
     * 构建文章状态Term查询
     */
    private Query buildArticleStatusTermQuery(String articleStatus) {
        return new Query.Builder()
                .term(
                        new TermQuery.Builder()
                                .field("articleStatus")
                                .value(
                                        new FieldValue.Builder()
                                                .stringValue(articleStatus)
                                                .build()
                                )
                                .build()
                )
                .build();
    }

    /**
     * 构建文章类型Term查询
     */
    private Query buildArticleTypeTermQuery(String articleType) {
        return new Query.Builder()
                .term(
                        new TermQuery.Builder()
                                .field("articleType")
                                .value(
                                        new FieldValue.Builder()
                                                .stringValue(articleType)
                                                .build()
                                )
                                .build()
                )
                .build();
    }

    /**
     * 构建站点IDTerm查询
     */
    private Query buildSiteIdTermQuery(String siteId) {
        return new Query.Builder()
                .term(
                        new TermQuery.Builder()
                                .field("siteId")
                                .value(
                                        new FieldValue.Builder()
                                                .stringValue(siteId)
                                                .build()
                                )
                                .build()
                )
                .build();
    }

}
